eletrotupi / tcc/ frontend/app/sleep/[id].tsx4db43b4346d1ef046417a8d12030155b577ed0c1
7.2 KB | 287 lines Raw
1
import { useState, useEffect, use } from "react";
2
import {
3
  StyleSheet,
4
  Text,
5
  View,
6
  ScrollView,
7
  Pressable,
8
  ActivityIndicator,
9
} from "react-native";
10
import { useLocalSearchParams, router, Stack } from "expo-router";
11
import { format } from "date-fns";
12
import { ptBR } from "date-fns/locale";
13

14
import { Card, SubtleInfoCard } from "@/components/ui/Cards";
15
import { Badge } from "@/components/ui/Badge";
16
import { Button } from "@/components/ui/Button";
17
import { Col, Between, Row } from "@/components/ui/LayoutHelpers";
18
import { Section, SectionHeader } from "@/components/ui/Sections";
19
import { Theme, Colors, Spacing } from "@/constants/theme";
20
import { MaterialIcons } from "@expo/vector-icons";
21
import {
22
  useSleepRecord,
23
  useDeleteSleepRecord,
24
} from "@/hooks/useSleepRecord.queries";
25
import { formatDate } from "@/lib/utils/time";
26

27
export default function SleepDetailScreen() {
28
  const { id } = useLocalSearchParams<{ id: string }>();
29
  const { data: sleepRecord, isLoading, isError } = useSleepRecord(id);
30
  const deleteSleepRecord = useDeleteSleepRecord();
31
  const [minutesLeft, setMinutesLeft] = useState(0);
32
  const [canDelete, setCanDelete] = useState(false);
33

34
  useEffect(() => {
35
    const updateTimer = () => {
36
      const createdAt = sleepRecord?.createdAt;
37
      if (!createdAt) return;
38

39
      const msSinceCreation = Date.now() - createdAt.getTime();
40
      const deletionWindowMs = 5 * 60 * 1000;
41

42
      const newCanDelete = msSinceCreation < deletionWindowMs;
43
      const newMinutesLeft = Math.max(
44
        0,
45
        Math.ceil((deletionWindowMs - msSinceCreation) / 60000),
46
      );
47

48
      setCanDelete(newCanDelete);
49
      setMinutesLeft(newMinutesLeft);
50
    };
51

52
    if (isLoading) return;
53

54
    updateTimer(); // Set initial values
55

56
    const interval = setInterval(updateTimer, 1000);
57

58
    return () => clearInterval(interval);
59
  }, [sleepRecord, isLoading]);
60

61
  console.log("sleepRecord", sleepRecord)
62
  console.log("isLoading", isLoading)
63

64
  if (isLoading) {
65
    return (
66
      <View style={styles.centered}>
67
        <ActivityIndicator size="large" color={Colors.light.tint} />
68
      </View>
69
    );
70
  }
71

72
  if (isError || !sleepRecord) {
73
    return (
74
      <View style={styles.centered}>
75
        <Text style={styles.errorText}>Registro não encontrado.</Text>
76
        <Pressable onPress={() => router.back()} style={styles.backButton}>
77
          <Text style={styles.backButtonText}>Voltar</Text>
78
        </Pressable>
79
      </View>
80
    );
81
  }
82

83
  const handleDelete = async () => {
84
    await deleteSleepRecord.mutateAsync(String(sleepRecord.id));
85

86
    router.back();
87
  };
88

89
  const deleteButtonMsg = () => {
90
    const msg = "Excluir Registro";
91
    const timeLeftStr = `(Disponível por ${minutesLeft < 1 ? "<1" : minutesLeft}:00)`;
92
    const suffix = canDelete ? timeLeftStr : "(Expirado)";
93

94
    return `${msg} ${suffix}`;
95
  };
96

97
  return (
98
    <>
99
      <Stack.Screen options={{ title: "Ver detalhes" }} />
100

101
      <ScrollView
102
        style={styles.container}
103
        contentContainerStyle={styles.content}
104
        showsVerticalScrollIndicator={false}
105
      >
106
        {/* Header card */}
107
        <Card style={styles.headerCard}>
108
          <Row style={{ alignItems: "center" }}>
109
            <View style={[styles.iconWrap, { backgroundColor: '#ede9fe' }]}>
110
              <MaterialIcons name="bedtime" size={22} color="#7c3aed" />
111
            </View>
112
            <Col>
113
              <Row style={{ alignItems: "flex-end" }}>
114
                <Text style={styles.averageLabel}>{sleepRecord.average}h</Text>
115
                <Text style={styles.suffixLabel}>de sono</Text>
116
              </Row>
117

118
              <Text style={styles.dateLabel}>
119
                {formatDate(new Date(sleepRecord.date))}
120
              </Text>
121
            </Col>
122
          </Row>
123
        </Card>
124

125
        {/* Annotation / notes */}
126
        {sleepRecord.annotations && (
127
          <Section>
128
            <SectionHeader title="Anotações" variant="subtle" />
129
            <Card style={{ padding: Spacing.cardGap }}>
130
              <Text style={styles.annotation}>
131
                "{sleepRecord.annotations?.trim()}"
132
              </Text>
133
            </Card>
134
          </Section>
135
        )}
136

137
        {/* Delete */}
138
        <View style={styles.section}>
139
          <Text style={styles.sectionLabel}>GESTÃO DO REGISTRO</Text>
140
          <Button
141
            variant="danger"
142
            isLoading={deleteSleepRecord.isPending}
143
            disabled={!canDelete || deleteSleepRecord.isPending}
144
            onPress={handleDelete}
145
            title={deleteButtonMsg()}
146
          />
147

148
          <SubtleInfoCard
149
            style={styles.deleteInfo}
150
            text={`
151
              A exclusão de registros é permitida apenas nos primeiros 5 minutos
152
              após o envio para garantir a integridade do histórico emocional.
153
              `}
154
          />
155
        </View>
156
      </ScrollView>
157
    </>
158
  );
159
}
160

161
const styles = StyleSheet.create({
162
  container: {
163
    flex: 1,
164
    backgroundColor: Theme.colors.background,
165
  },
166
  content: {
167
    padding: Spacing.containerPadding,
168
    gap: Spacing.sectionGap,
169
    paddingBottom: 48,
170
  },
171
  iconWrap: {
172
    width: 40,
173
    height: 40,
174
    borderRadius: 20,
175
    alignItems: 'center',
176
    justifyContent: 'center',
177
    marginTop: 1
178
  },
179
  headerCardContent: {
180
    flexDirection: "row",
181
    alignItems: "center",
182
    gap: 8,
183
  },
184
  averageLabel: {
185
    height: '100%',
186
    fontSize: 24,
187
    fontWeight: "700",
188
    color: Colors.light.text,
189
  },
190
  averageLabelSuffix: {
191
    height: '100%',
192
    fontSize: 17,
193
    fontWeight: "700",
194
    color: Colors.light.textSecondary,
195
  },
196
  centered: {
197
    flex: 1,
198
    justifyContent: "center",
199
    alignItems: "center",
200
    gap: 12,
201
  },
202
  errorText: {
203
    fontSize: 15,
204
    color: Colors.light.textSecondary,
205
  },
206
  backButton: {
207
    paddingHorizontal: 20,
208
    paddingVertical: 10,
209
    backgroundColor: Theme.colors.tint,
210
    borderRadius: Theme.borderRadius.md,
211
  },
212
  backButtonText: {
213
    fontWeight: "700",
214
    color: Theme.colors.text,
215
  },
216

217
  // Header
218
  headerCard: {
219
    padding: 16,
220
  },
221
  moodIcon: {
222
    fontSize: 40,
223
  },
224
  moodLabel: {
225
    fontSize: 17,
226
    fontWeight: "700",
227
    color: Colors.light.text,
228
  },
229
  moodTime: {
230
    fontSize: 13,
231
    color: Colors.light.textSecondary,
232
  },
233

234
  // Sections
235
  section: {
236
    gap: 8,
237
  },
238
  sectionLabel: {
239
    fontSize: 11,
240
    fontWeight: "600",
241
    letterSpacing: 0.8,
242
    color: Colors.light.textSecondary,
243
    textTransform: "uppercase",
244
  },
245

246
  // Components
247
  componentRow: {
248
    flexDirection: "row",
249
    justifyContent: "space-between",
250
    alignItems: "center",
251
    paddingVertical: 10,
252
  },
253
  componentLabel: {
254
    fontSize: 15,
255
    fontWeight: "700",
256
    color: Colors.light.text,
257
  },
258
  componentIntensity: {
259
    fontSize: 13,
260
    color: Colors.light.textSecondary,
261
  },
262
  componentIntensityGreen: {
263
    fontSize: 13,
264
    fontWeight: "700",
265
    color: Theme.colors.tint,
266
  },
267
  progressTrack: {
268
    height: 6,
269
    backgroundColor: Theme.colors.light.divider,
270
    borderRadius: Theme.borderRadius.full,
271
    marginBottom: 4,
272
    overflow: "hidden",
273
  },
274
  progressFill: {
275
    height: "100%",
276
    backgroundColor: Theme.colors.light.tint,
277
    borderRadius: Theme.borderRadius.full,
278
  },
279
  // Annotation
280
  annotation: {
281
    fontSize: 14,
282
    lineHeight: 22,
283
    color: Colors.light.textSecondary,
284
    fontStyle: "italic",
285
    padding: 4,
286
  },
287
});